#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "UT_Lib.h"
#include "CFM_Lib.h"
#include "HF_Lib.h"
#include "AM_Lib.h"
#include "SCM_Lib.h"
#include "UT_DM_Defines.h"

static void PrintRecord ( char *record, attrDesc *attrs, int attibuteCount );
static int len ( char *string, int max );

/*argv[0]="create"

*    argv[1]=όνομα-σχέσης

*    argv[2]=όνομα-πεδίου-1

*    argv[3]=φορμάτ-πεδίου-1

*        .

*        .

*        .

*    argv[argc-2]=όνομα-πεδίου-Ν

*    argv[argc-1]=φορμάτ-πεδίου-Ν

*    argv[argc]=NULL

*

*

*Η συνάρτηση αυτή καλείται όταν ο χρήστης πληκτρολογήσει μια εντολή CREATE. 
*Για τη δημιουργία της νέας σχέσης, η υλοποίησή σας θα πρέπει να χρησιμοποιεί 
*την συνάρτηση HF_CreateFile. Επιπλέον θα πρέπει να ενημερώνει τους καταλόγους 
*(σχέσεις relCat και attrCat, κλπ) της βάσης. Δηλαδή, θα πρέπει να εισάγει μία 
*εγγραφή στη σχέση relCat, η οποία να περιγράφει τα χαρακτηριστικά της νέας σχέσης.
*Επιπρόσθετα, θα πρέπει να εισάγει στη σχέση attrCat τις αντίστοιχες εγγραφές που θα
*περιγράφουν ένα-ένα τα γνωρίσματα της νέας σχέσης.
*/
int UT_create ( int argc, char *argv[] )
{
	relDesc reldesc;
	attrDesc *attrdesc;
	int i, sum = 0, attrdesc_pos = 0, offset = 0, length = 0;
	char ch[6], type;
	
	/* Έλεγχος των ορισμάτων. */

	if ( ( UTE_errno = SCM_create(argc, argv) ) < 0 )
	{
		printf("error in UT_create\n");
		return UTE_errno;
	}
	
	/* Τοποθέτηση του ονόματος της σχέσης στην relDesc. */

	strcpy ( reldesc.relname, argv[1] );
	
	/* Εύρεση του relwidth. */

	for ( i = 3; i < argc; i += 2 )
	{
		strcpy (ch, argv[i]);
		
		if ( !strncmp ( ch, "'i", 2) ||  !strncmp ( ch, "'f", 2 ) )
		{
			sum += 4;
		}
		else		//== c
		{
			sum += atoi ( argv[i] + 2 );	//mia thesi deksia na agnohsei to 'c'
		}
	}
	
	///create student(id = 'i', name = 'c20');
	reldesc.relwidth = sum;
	reldesc.attrcnt = argc / 2 - 1;
	reldesc.indexcnt = 0;
	
	//desmeusi mnimis gia to attrdesc
	attrdesc = malloc( reldesc.attrcnt* sizeof(attrdesc) );

	//gemisma twn attributes
	for (i = 2; i < argc ; i += 2, attrdesc_pos++ )
	{	
		strcpy ( attrdesc[attrdesc_pos].relname , argv[1] );
		strcpy ( attrdesc[attrdesc_pos].attrname, argv[i] );
		attrdesc[attrdesc_pos].offset = offset;
		
		//ipologisei to length
		if ( !strcmp ( argv[i+1], "'i'" )  )
		{
			length = 4;
			type = 'i';
		}
		else if (  !strcmp(argv[i+1], "'f'") )
		{
			length = 4;
			type = 'f';
		}
		else
		{
			length = atoi ( argv[i+1] + 2);	//mia thesi deksia na agnohsei to 'c'
			type = 'c';
		}
		
		//to prostheti sto offset
		offset += length;
		attrdesc[attrdesc_pos].attrlength = length;
		attrdesc[attrdesc_pos].attrtype = type;
		attrdesc[attrdesc_pos].indexed = 0;
		attrdesc[attrdesc_pos].indexno = -1;
	}

	if ( ( UTE_errno = CFM_AddRel ( reldesc, attrdesc ) ) < 0 )
	{
		return UTE_errno;
	}
	
	if ( ( UTE_errno = HF_CreateFile ( argv[1] ) ) < 0 ) 
	{
		HF_PrintError("error in UT create");
		return UTE_errno;
	}
	return UTE_errno = UTE_OK;
}



/*argv[0]="buildindex"

*    argv[1]=όνομα-σχέσης

*    argv[2]=όνομα-πεδίου

*    argv[3]=NULL

*

*

*Η συνάρτηση αυτή καλείται όταν ο χρήστης πληκτρολογήσει μια εντολή BUILDINDEX. 
*Η υλοποίηση της εντολής αυτής θα πρέπει να δημιουργεί το ευρετήριο χρησιμοποιώντας 
*την συνάρτηση AM_CreateIndex και να ενημερώνει κατάλληλα τους καταλόγους της βάσης.
*/

int UT_buildindex ( int argc, char *argv[] )
{
	int i;	
	relDesc reldesc;
	attrDesc * attrs;

	/* Έλεγχος ορισμάτων. */

	if ( ( UTE_errno = SCM_buildIndex ( argc, argv ) ) < 0 )
	{
		return UTE_errno;
	}

	/* προσκόμηση των πληροφοριών απο τους καταλόγους για τον πίνακα με όνομα argv[1]. */

	if ( ( UTE_errno = CFM_Lookup ( argv[1], &reldesc, &attrs ) ) < 0 )
	{
		return UTE_errno;
	}
	
	for ( i = 0; i < reldesc.attrcnt; i++)
	{
		/* Ψάχνουμε τις πληροφορίες για το attribute με όνομα argv[2]. */

		if ( !strcmp ( attrs[i].attrname , argv[2] ) )
		{
			int filedesc, amdesc, scandesc, recId;
			char *amname, *rec;
		
			/* Δημιουργία του ευρετηρίου. */

			UTE_errno = AM_CreateIndex ( argv[1], reldesc.indexcnt, attrs[i].attrtype , attrs[i].attrlength );
		
			if ( UTE_errno < 0 )
			{
				return UTE_errno;
			}

			/* Ενημέρωση των καταλόγων της βάσης. */

			if ( ( UTE_errno = CFM_IndexAttribute ( argv[1], argv[2], reldesc.indexcnt ) ) < 0 )
			{
				return UTE_errno;
			}
			
			/* Άνοιγμα του αρχείου που απεικονίζει τον πίνακα στον δίσκο. */ //πσσσσ + 1 μόριο μόνο και μόνο για αυτό το σχόλιο :P ( πλάκα κάνω -.- )

			if ( ( UTE_errno = HF_OpenFile ( argv[1] ) ) < 0 )
			{
				return UTE_errno;
			}

			filedesc = UTE_errno;
			
			/* "Χτίσιμο" του ονόματος του ευρετηρίου. */

			amname = malloc ( MAXNAME + reldesc.attrcnt + 1 );	//to onoma mporei na einai mexri MAXNAME kai ton arithmo twn pediwn
			sprintf ( amname, "%s.%d", argv[1], reldesc.indexcnt );

			/* Άνοιγμα του ευρετηρίου. */

			if ( ( UTE_errno = HF_OpenFile ( amname ) ) < 0 )
			{
				return UTE_errno;
			}

			amdesc = UTE_errno;
			
			/* Άνοιγμα του scan για να φέρουμε όλες τις εγγραφές του πίνακα. */

			scandesc = HF_OpenFileScan ( filedesc, reldesc.relwidth, attrs[i].attrtype, attrs[i].attrlength, attrs[i].offset, EQUAL, NULL );
			
			if ( scandesc < 0 )
			{
				return UTE_errno;
			}

			rec = malloc ( sizeof(reldesc.relwidth) );
			
			/* Προσκόμηση όλων των πλειάδων του πίνακα που θα ευρετηριάσουμε το πεδίο,
			 *και εισαγωγή τους στο ευρετήριο που φτιάξαμε.
			 */

			while ( ( recId = HF_FindNextRec ( scandesc, rec ) ) >= 0 )
			{
				if ( ( UTE_errno = AM_InsertEntry ( amdesc, attrs[i].attrtype, attrs[i].attrlength, rec, recId ) ) < 0 )
				{
					return UTE_errno;
				}
			}
			
			/* Κλείσιμο του scan που ανοίξαμε πριν .*/		
	
			if ( ( UTE_errno = HF_CloseFileScan ( scandesc ) ) < 0 )
			{
				return UTE_errno;
			}
			
			/* Κλείσιμο του ευρετηρίου. */

			if ( ( UTE_errno = HF_CloseFile ( amdesc ) ) < 0 )
			{
				return UTE_errno;
			}
			
			/* Κλείσιμο του αρχείου που αντιχτοισεί στον πίνακα. */

			if ( ( UTE_errno = HF_CloseFile ( filedesc ) ) < 0 ) 
			{
				return UTE_errno;
			}
			
			return UTE_errno = UTE_OK;
		}
	}
	
	return UTE_errno = UTE_NOT_FOUND;
}

/*argv[0]="destroy"

 *   argv[1]=όνομα-σχέσης

 *   argv[2]=NULL

 *

 *Η συνάρτηση αυτή καλείται όταν ο χρήστης πληκτρολογήσει μια εντολή DESTROY. 
 *Για την υλοποίηση αυτής της εντολής θα πρέπει να χρησιμοποιήσετε τις συναρτήσεις
 *AM_DestroyIndex και HF_DestroyFile και να ενημερώσετε κατάλληλα τους καταλόγους της βάσης.
 */

int UT_destroy ( int argc, char *argv[] ) 
{
	int scandesc;
	attrDesc attrdesc;

	/* Έλεγχος των ορισμάτων. */

	if ( ( UTE_errno = SCM_destroy ( argc, argv ) ) < 0 )
	{
		return UTE_errno;
	}

	/* 'Ανοιγμα του αρχείου που απεικονίζει τον πίνακα στο δίσκο. */

	scandesc = HF_OpenFileScan(attrCatFd, sizeof(attrDesc), 'c', MAXNAME, 0, EQUAL,argv[1]);

	if ( scandesc < 0 )
	{
		HF_PrintError ( "error in UTE destroy " );
		return UTE_errno = UTE_HF_ERROR;	 	
	}
	
	/* Προσκόμηση όλων των attributes της σχέσης. */

	while ( HF_FindNextRec ( scandesc, (char *) &attrdesc ) >= 0 )
	{
		if ( attrdesc.indexed )
		{
			/* Αν είναι ευρετηριασμένο το attribute, διαγράφουμε το ευρετήριο. */

			if ( ( UTE_errno = AM_DestroyIndex ( argv[1], attrdesc.indexno ) ) < 0 )
			{
				return UTE_errno;
			}
		}
	}
	
	/* Κλείσιμο του αρχείου του πίνακα. */

	if ( UTE_errno = HF_CloseFileScan ( scandesc ) < 0 )
	{
		HF_PrintError ( "error in UTE destroy " );
		return UTE_errno = UTE_HF_ERROR;	
	}
		
	if ( ( UTE_errno = CFM_DeleteRel ( argv[1] ) ) < 0 )
	{
		return UTE_errno;
	}
	
	if ( ( UTE_errno = HF_DestroyFile ( argv[1]) ) < 0 )
	{
		HF_PrintError ( "error in UTE destroy " );
		return UTE_errno = UTE_HF_ERROR;
	}
	
	return UTE_errno = UTE_OK;
}




/*Η συνάρτηση αυτή καλείται όταν ο χρήστης πληκτρολογήσει την εντολή QUIT.
 *Η συνάρτηση αυτή θα πρέπει να κλείνει όλες τις τυχόν ανοικτές σχέσεις και ευρετήρια
 *(συμπεριλαμβανομένων και αυτών που αποτελούν τους καταλόγους της βάσης) και στην συνέχεια να
 *καλεί την exit(1).
 */

int UT_quit ( void )
{
	/* Κλείσιμο των αρχείων καταλόγων και έξοδος. */

	if ( ( HF_CloseFile ( relCatFd ) ) < 0 )
	{
		HF_PrintError ( "error in UT QUIT" );
		return UTE_errno = UTE_HF_ERROR;
	}
	
	if ( ( UTE_errno = HF_CloseFile ( attrCatFd ) ) < 0 )
	{
		HF_PrintError ( "error in UT QUIT" );
		return UTE_errno = UTE_HF_ERROR;
	}

	UTE_errno = UTE_OK;

	exit ( 0 );
}

/*Η συνάρτηση αυτή εκτυπώνει στην οθόνη τις εγγραφές της σχέσης relName.

 *Η συνάρτηση αυτή χρησιμοποιείται από τις DM_select και DM_join σε περίπτωση 

 *που το αποτέλεσμα πρέπει να εκτυπωθεί στην οθόνη.

 */

int UT_print ( char *relName )
{
	char *record;
	relDesc reldesc;
	attrDesc *attrs;
	int fileDesc, scanDesc, recordSize, recId;

	/* Έλεγχος των ορισμάτων. */

	if ( ( UTE_errno = SCM_print ( relName ) ) < 0 )
	{
		return UTE_errno;
	}

	/* Άνοιγμα του αρχείου που απεικονίζει τον πίνακα relName και άνοιγμα
	 *scan για να φέρω όλες τις εγγραφές του πίνακα.
	 */

	if ( ( fileDesc = HF_OpenFile ( relName ) ) < 0 )
	{
		HF_PrintError ( "UT Print" );
		return UTE_errno = fileDesc;
	}

	/* Προσκόμηση των πληροφοριών απο τους καταλόγους της βάσης για τον
	 *ζητούμενο πίνακα.
	 */

	/* Προσκόμηση και εκτύπωση όλως των εγγραφών απο το αρχείο. */

	if ( ( UTE_errno = CFM_Lookup ( relName, &reldesc, &attrs ) ) < 0 )
	{
		return UTE_errno;
	}

	recordSize = reldesc.relwidth;
	record = malloc ( sizeof(char) * recordSize );

	/* Εκτύπωση του ονόματος του πίνακα. */

	printf ( "%s", relName );

	/* Προσκόμηση της πρώτης εργγραφής του πίνακα και εκτύπωσή της. */

	recId = HF_GetFirstRec ( fileDesc, record, recordSize );

	if ( recId < 0 )
	{
		if ( recId == HFE_EOF )
		{
			printf( "Empty set\n" );
			return UTE_OK;
		}
		else
		{
			HF_PrintError ( "UT print" );
			return UTE_errno = recId;
		}
	}

	PrintRecord ( record, attrs, reldesc.attrcnt );

	/* Προσκόμηση και εκτύπωση ολων των εγγραφών. */

	while ( ( recId = HF_GetNextRec ( fileDesc, recId, record, recordSize ) ) >= 0 )
	{
		PrintRecord ( record, attrs, reldesc.attrcnt );
	}


	/* Κλείσιμο του αρχειου. */

	if ( ( HF_errno = HF_CloseFile ( fileDesc ) )  < 0 )
	{
		return UTE_errno;
	}

	return HFE_OK;
}

void PrintRecord ( char *record, attrDesc *attrs, int attibuteCount )
{
	int  i, attributeToPrint = 0;

	for ( i = 0; i < attibuteCount; i++ )
	{
		if ( attrs[i].attrtype == 'c' )
		{
			int offset;
			offset = attrs[i].offset;
			
			putchar ( ' ' );
			fwrite ( &record[offset], sizeof ( char ), len ( &record[offset], MAXNAME ), stdout ); 
			putchar ( ' ' );			
		}
		else if ( attrs[i].attrtype == 'i'  )
		{
			int offset, data;
			offset = attrs[i].offset;

			memcpy(&data, &record[offset], sizeof(int));
			printf ( " %d ", data );
		}
		else if ( attrs[i].attrtype == 'f'  )
		{
			int offset;
			float data;
			offset = attrs[i].offset;

			memcpy(&data, &record[offset], sizeof(float));
			printf ( " %f ", data );
		}
	}
}

/* Συνάρτηση που μετραει το μέγεθος του string ( τερματιζόμενο απο 0. */
static int len ( char *string, int max )
{
	int i;

	for ( i = 0 ; i != max ; i++ )
	{
		if ( !string[i] )
			break;
	}
	return i;
}



